﻿using System.Threading;
using System.Net.Sockets;
using System.Collections.Generic;
using System;
using Server.Tools;
using System.Net;
using System.Text;

namespace Server.Net
{
    /// <summary>
    /// 此类为应用程序服务器提供网络服务，管理所有的网络资源
    /// </summary>
    class NetService
    {
        /// <summary>
        /// 设计的最大连接数
        /// </summary>
        private int maxConnectionNumber;

        /// <summary>
        /// 各个SocketAsyncEventArgs的缓冲区大小
        /// </summary>
        private static int SocketAsyncEventArgs_BufferSize = Properties.Settings.Default.SocketAsyncEventArgs_BufferSize;

        /// <summary>
        /// 缓冲区池
        /// </summary>
        private BufferManager bufferManager;

        /// <summary>
        /// 监听Socket
        /// </summary>
        private Socket listenSocket;

        /// <summary>
        /// SocketAsyncEventArgs池
        /// </summary>
        private SocketAsyncEventArgsPool readWritePool;

        /// <summary>
        /// SocketAsyncEventArgsPool的大小
        /// </summary>
        private int socketAsyncEventArgsPoolSize;

		/// <summary>
        /// 对Net进行统计的实例
        /// </summary>
        private NetCounter netCounter;

        /// <summary>
        /// 限制同一时间访问资源的线程数
        /// </summary>
        private Semaphore m_maxNumberAcceptedClients;

        /// <summary>
        /// 用户名与接收SocketAsyncEventArgs之间的映射
        /// </summary>
        internal IDictionary<string, SocketAsyncEventArgs> userMap;

        /// <summary>
        /// 储存管理登录失败信息
        /// </summary>
        internal FailLoginInfo failLoginInfo;
		
        /// <summary>
        /// 构造函数，初始化所有资源
        /// </summary>
        public NetService()
        {
           
            try
            {
                userMap = new Dictionary<string, SocketAsyncEventArgs>();
                failLoginInfo = new FailLoginInfo();
                maxConnectionNumber = Properties.Settings.Default.MaxConnectionNumber;
                socketAsyncEventArgsPoolSize = Properties.Settings.Default.SocketAsyncEventArgsPoolSize;

                bufferManager = new BufferManager(socketAsyncEventArgsPoolSize, SocketAsyncEventArgs_BufferSize);

                readWritePool = new SocketAsyncEventArgsPool(socketAsyncEventArgsPoolSize);

                m_maxNumberAcceptedClients = new Semaphore(maxConnectionNumber, maxConnectionNumber);
				
				netCounter = new NetCounter();

                SocketAsyncEventArgs readWriteEventArg;
                for (int i = 0; i < socketAsyncEventArgsPoolSize; i++)
                {
                    readWriteEventArg = new SocketAsyncEventArgs();
                    readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                    readWriteEventArg.UserToken = new AsyncUserToken();

                    bufferManager.SetBuffer(readWriteEventArg);

                    readWritePool.Push(readWriteEventArg);
                }
            }
            catch (Exception e)
            {
                LogService.Instance.Log.Error("NetService初始化失败！" + e.ToString());
            }
        }

        /// <summary>
        /// 开始服务
        /// </summary>
        public void StartService()
        {
            try
            {
                string HostName = Dns.GetHostName();
                IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
                IPAddress[] addrs = IpEntry.AddressList;
                IPAddress ipAddress = null;
                foreach (IPAddress ip in addrs)
                {
                    if (ip.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ipAddress = IPAddress.Parse(ip.ToString());
                    }
                }
                //ipAddress = IPAddress.Parse("127.0.0.1");
                IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Properties.Settings.Default.ListenPort);
                
                listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                listenSocket.Bind(localEndPoint);
                listenSocket.Listen(100);
                LogService.Instance.Log.Info("开始监听....");
                StartAccept(null);
            }
            catch (Exception e)
            {
                LogService.Instance.Log.Error("StartService()出错！" + e.ToString());
            }
        }

        /// <summary>
        /// 接受新的连接请求
        /// </summary>
        /// <param name="acceptEventArg">此SocketAsyncEventArgs实例会一直用于传递新连接</param>
        public void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            try
            {
                if (acceptEventArg == null)
                {
                    acceptEventArg = new SocketAsyncEventArgs();
                    acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
                }
                else
                {
                    acceptEventArg.AcceptSocket = null;
                }

                m_maxNumberAcceptedClients.WaitOne();
                bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
                if (!willRaiseEvent)
                {
                    ProcessAccept(acceptEventArg);
                }
            }
            catch (Exception e)
            {
                LogService.Instance.Log.Error("接受新的连接出错！" + e.ToString());
            }
        }

        /// <summary>
        /// 当有新的连接请求时触发
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }

        /// <summary>
        /// 处理连接请求并开启对该Socket的读取服务
        /// </summary>
        /// <param name="e"></param>
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            try
            {
                Interlocked.Increment(ref netCounter.currentConnectedSocketsNumber);
				LogService.Instance.Log.Info("新连接，连接来自" + e.AcceptSocket.RemoteEndPoint);
                SocketAsyncEventArgs readEventArgs = readWritePool.Pop();
                readEventArgs.AcceptSocket = e.AcceptSocket;
                bool willRaiseEvent = readEventArgs.AcceptSocket.ReceiveAsync(readEventArgs);
                if (!willRaiseEvent)
                {
                    ProcessReceive(readEventArgs, readEventArgs.BytesTransferred);
                }

                StartAccept(e);
            }
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("处理新连接出错！" + ee.ToString());
            }
        }

        /// <summary>
        /// 接收到数据包或者发送完成数据包都会触发此函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
					ProcessReceive(e, e.BytesTransferred);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                default:
                    LogService.Instance.Log.Warn("LastOperation:" + e.LastOperation + " 异常！");
                    break;
            }
        }

        /// <summary>
        /// 整理接收到的数据包
		/// 根据数据包前四个字节识别是否接收到了完整的数据包，如果接收完整了的数据包就调用处理数据包函数
        /// </summary>
        /// <param name="e"></param>
        private void ProcessReceive(SocketAsyncEventArgs e, int bytesRead)
        {
			LogService.Instance.Log.Info("开始整理数据包");
            try
			{
                AsyncUserToken token = (AsyncUserToken)e.UserToken;
                if (bytesRead > 0 && e.SocketError == SocketError.Success)
                {
                    Interlocked.Add(ref netCounter.totalBytesRead, bytesRead);

                    if (token.State == AsyncUserToken.Begin)
                    {
                        if (bytesRead < 4)
                        {
                            LogService.Instance.Log.Error("数据包只读取到少于四字节的数据！");
                            return;
                        }
                        token.PackageLength = BitConverter.ToInt32(e.Buffer, e.Offset);
                        if (token.PackageLength < 9)
                        {
                            LogService.Instance.Log.Error("数据包的长度(packageLength = " + token.PackageLength + ")异常！");
                            return;
                        }
                        if (token.PackageLength > SocketAsyncEventArgs_BufferSize)
                        {
                            LogService.Instance.Log.Info("数据包的长度(packageLength = " + token.PackageLength + ")超出了一般的长度！");
                            token.Buffer = new byte[token.PackageLength];
                            Array.Copy(e.Buffer, e.Offset + token.Offset, token.Buffer, 0, bytesRead);
                            token.Offset = bytesRead;
                            token.ReceiveDataLength = bytesRead;
                            token.State = AsyncUserToken.WaitNextSpillOverPackagePart;
                            int toStore = (token.PackageLength - token.ReceiveDataLength) < SocketAsyncEventArgs_BufferSize ? (token.PackageLength - token.ReceiveDataLength) : SocketAsyncEventArgs_BufferSize;
                            e.SetBuffer(e.Offset, toStore);
                            e.AcceptSocket.ReceiveAsync(e);
                            return;
                        }

                        if (bytesRead < token.PackageLength)
                        {
                            LogService.Instance.Log.Info("接收到了不完整的数据包");
                            token.Offset += bytesRead;
                            token.ReceiveDataLength = bytesRead;
                            token.State = AsyncUserToken.WaitNextPackagePart;
                            LogService.Instance.Log.Debug("(offset = " + e.Offset + ")");
                            e.SetBuffer(e.Offset + token.Offset, token.PackageLength - token.ReceiveDataLength);
                            LogService.Instance.Log.Debug("(offset = " + e.Offset + ")");
                            e.AcceptSocket.ReceiveAsync(e);
                            return;
                        }
                        else if (bytesRead > token.PackageLength)
                        {
                            LogService.Instance.Log.Info("接收到了溢出的数据包");
                            ProcessPackage(e);
                            token.State = AsyncUserToken.Begin;
                            token.Offset += token.PackageLength;
                            bytesRead -= token.PackageLength;
                            ProcessReceive(e, bytesRead);
                            return;
                        }
                        else	// (bytesRead == packageLength)
                        {
                            ProcessPackage(e);
                            token.Offset = 0;
                            e.SetBuffer(e.Offset, SocketAsyncEventArgs_BufferSize);
                            e.AcceptSocket.ReceiveAsync(e);
                            return;
                        }
                    }
                    else if (token.State == AsyncUserToken.WaitNextPackagePart)
                    {
                        if (bytesRead + token.ReceiveDataLength < token.PackageLength)
                        {
                            LogService.Instance.Log.Info("再次接收到了不完整的数据包");
                            token.Offset += bytesRead;
                            token.ReceiveDataLength += bytesRead;
                            int toStore = (token.PackageLength - token.ReceiveDataLength) < SocketAsyncEventArgs_BufferSize ? (token.PackageLength - token.ReceiveDataLength) : SocketAsyncEventArgs_BufferSize;
                            e.SetBuffer(e.Offset + token.Offset, toStore);
                            e.AcceptSocket.ReceiveAsync(e);
                            return;
                        }
                        else if (bytesRead + token.ReceiveDataLength == token.PackageLength)
                        {
                            token.Offset -= token.ReceiveDataLength;
                            ProcessPackage(e);
                            token.Offset = 0;
                            e.SetBuffer(e.Offset, SocketAsyncEventArgs_BufferSize);
                            token.State = AsyncUserToken.Begin;
                            e.AcceptSocket.ReceiveAsync(e);
                        }
                    }
                    else if (token.State == AsyncUserToken.WaitNextSpillOverPackagePart)
                    {
                        if (bytesRead + token.ReceiveDataLength < token.PackageLength)
                        {
                            LogService.Instance.Log.Info("继续接收超长数据包");
                            Array.Copy(e.Buffer, e.Offset, token.Buffer, token.Offset, bytesRead);
                            token.Offset += bytesRead;
                            token.ReceiveDataLength += bytesRead;
                            e.SetBuffer(e.Offset, token.PackageLength - token.ReceiveDataLength);
                            e.AcceptSocket.ReceiveAsync(e);
                            return;
                        }
                        else if (bytesRead + token.ReceiveDataLength == token.PackageLength)
                        {
                            token.Offset = 0;
                            ProcessPackage(e);
                            token.Buffer = null;
                            e.SetBuffer(e.Offset, SocketAsyncEventArgs_BufferSize);
                            token.State = AsyncUserToken.Begin;
                            e.AcceptSocket.ReceiveAsync(e);
                        }
                    }
                }
                else
                {
                    CloseClientSocket(e);
                }
			}
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("整理数据包过程出错！" + ee.ToString());
            }
        }

        /// <summary>
        /// 分发数据包到登录前或登录后的处理函数
        /// </summary>
        /// <param name="e"></param>
        private void ProcessPackage(SocketAsyncEventArgs e)
        {
            try 
            {
                AsyncUserToken token = (AsyncUserToken)e.UserToken;
                if (token.UserName == null)
                {
                    new ProcessReceiveBeforeLogin(this).ProcessPackage(e);
                }
                else
                {
                    new ProcessReceiveAfterLogin(this).ProcessPackage(e);
                }
            }
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("分发数据包过程出错！" + ee.ToString());
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="socket">使用的Socket</param>
        /// <param name="packageIdentifier">响应包裹号</param>
        /// <param name="code">命令代码</param>
        /// <param name="str">发送的数据</param>
        public void SendAsync(Socket socket, byte packageIdentifier, string code, string str)
        {
            LogService.Instance.Log.Info("To[" + socket.RemoteEndPoint + "] " + code + "---" + str);
            try
            {
                byte[] data = Encoding.Unicode.GetBytes(str);
                int packageLength = data.Length + 9;
                SocketAsyncEventArgs e = readWritePool.Pop();
                Array.Copy(BitConverter.GetBytes(packageLength), 0, e.Buffer, e.Offset, 4);
                e.Buffer[e.Offset + 4] = packageIdentifier;
                Encoding.ASCII.GetBytes(code, 0, 4, e.Buffer, e.Offset + 5);
                if (packageLength > SocketAsyncEventArgs_BufferSize)
                {
                    Array.Copy(data, 0, e.Buffer, e.Offset + 9, SocketAsyncEventArgs_BufferSize - 9);
                    socket.SendAsync(e);
                    int sendOffset = SocketAsyncEventArgs_BufferSize - 9;
                    LogService.Instance.Log.Info("发收长数据包");
                    while (data.Length - sendOffset > 0)
                    {
                        SocketAsyncEventArgs args = readWritePool.Pop();
                        int sendLength = (data.Length - sendOffset > SocketAsyncEventArgs_BufferSize) ? SocketAsyncEventArgs_BufferSize : data.Length - sendOffset;
                        Array.Copy(data, sendOffset, args.Buffer, args.Offset, sendLength);
                        args.SetBuffer(args.Offset, sendLength);
                        socket.SendAsync(args);
                        sendOffset += sendLength;
                    }
                }
                else
                {
                    Array.Copy(data, 0, e.Buffer, e.Offset + 9, packageLength - 9);
                    e.SetBuffer(e.Offset, packageLength);
                    socket.SendAsync(e);
                }
            }
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("发送数据包过程出错！" + ee.ToString());
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="socket">发送Socket</param>
        /// <param name="packageIdentifier"></param>
        /// <param name="code"></param>
        /// <param name="data"></param>
        public void SendAsync(Socket socket, byte packageIdentifier, string code, byte[] data)
        {
            LogService.Instance.Log.Info("To[" + socket.RemoteEndPoint + "] " + code + "--- <" + data.Length + ">");
            try
            {
                int packageLength = data.Length + 9;
                SocketAsyncEventArgs e = readWritePool.Pop();
                Array.Copy(BitConverter.GetBytes(packageLength), 0, e.Buffer, e.Offset, 4);
                e.Buffer[e.Offset + 4] = packageIdentifier;
                Encoding.ASCII.GetBytes(code, 0, 4, e.Buffer, e.Offset + 5);
                if (packageLength > SocketAsyncEventArgs_BufferSize)
                {
                    Array.Copy(data, 0, e.Buffer, e.Offset + 9, SocketAsyncEventArgs_BufferSize - 9);
                    socket.SendAsync(e);
                    int sendOffset = SocketAsyncEventArgs_BufferSize - 9;
                    LogService.Instance.Log.Info("发收长数据包");
                    while (data.Length - sendOffset > 0)
                    {
                        SocketAsyncEventArgs args = readWritePool.Pop();
                        int sendLength = (data.Length - sendOffset > SocketAsyncEventArgs_BufferSize) ? SocketAsyncEventArgs_BufferSize : data.Length - sendOffset;
                        Array.Copy(data, sendOffset, args.Buffer, args.Offset, sendLength);
                        args.SetBuffer(args.Offset, sendLength);
                        socket.SendAsync(args);
                        sendOffset += sendLength;
                    }
                }
                else
                {
                    Array.Copy(data, 0, e.Buffer, e.Offset + 9, packageLength - 9);
                    e.SetBuffer(e.Offset, packageLength);
                    socket.SendAsync(e);
                }
            }
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("发送数据包过程出错！" + ee.ToString());
            }
        }

        /// <summary>
        /// 异步发送数据
        /// </summary>
        /// <param name="socket">发送Socket</param>
        /// <param name="packageIdentifier"></param>
        /// <param name="code"></param>
        /// <param name="data"></param>
        public void SendAsync(Socket socket, byte packageIdentifier, string code, byte[] data, int index, int length)
        {
            LogService.Instance.Log.Info("To[" + socket.RemoteEndPoint + "] " + code + "--- <" + length + ">");
            try
            {
                int packageLength = length + 9;
                SocketAsyncEventArgs e = readWritePool.Pop();
                Array.Copy(BitConverter.GetBytes(packageLength), 0, e.Buffer, e.Offset, 4);
                e.Buffer[e.Offset + 4] = packageIdentifier;
                Encoding.ASCII.GetBytes(code, 0, 4, e.Buffer, e.Offset + 5);
                if (packageLength > SocketAsyncEventArgs_BufferSize)
                {
                    Array.Copy(data, index, e.Buffer, e.Offset + 9, SocketAsyncEventArgs_BufferSize - 9);
                    socket.SendAsync(e);
                    int sendOffset = index + SocketAsyncEventArgs_BufferSize - 9;
                    LogService.Instance.Log.Info("发送长数据包");
                    while (length + index - sendOffset > 0)
                    {
                        SocketAsyncEventArgs args = readWritePool.Pop();
                        int sendLength = (length + index - sendOffset > SocketAsyncEventArgs_BufferSize) ? SocketAsyncEventArgs_BufferSize : length + index - sendOffset;
                        Array.Copy(data, sendOffset, args.Buffer, args.Offset, sendLength);
                        args.SetBuffer(args.Offset, sendLength);
                        socket.SendAsync(args);
                        sendOffset += sendLength;
                    }
                }
                else
                {
                    Array.Copy(data, index, e.Buffer, e.Offset + 9, length);
                    e.SetBuffer(e.Offset, packageLength);
                    socket.SendAsync(e);
                }
            }
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("发送数据包过程出错！" + ee.ToString());
            }
        }

        /// <summary>
        /// 发送断线通知
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="code"></param>
        /// <param name="str">内容</param>
        public void SendAsyncRemove(string userName, string code, string str)
        {
            try
            {
                if (userMap.ContainsKey(userName))
                {
                    SocketAsyncEventArgs args = userMap[userName];
                    byte[] data = Encoding.Unicode.GetBytes(str);
                    int packageLength = data.Length + 9;
                    SocketAsyncEventArgs e = readWritePool.Pop();
                    Array.Copy(BitConverter.GetBytes(packageLength), 0, e.Buffer, e.Offset, 4);
                    e.Buffer[e.Offset + 4] = 0;
                    Encoding.ASCII.GetBytes(code, 0, 4, e.Buffer, e.Offset + 5);
                    if (packageLength > SocketAsyncEventArgs_BufferSize)
                    {
                        LogService.Instance.Log.Error("发送继开连接数据包过长！");
                    }
                    else
                    {
                        Array.Copy(data, 0, e.Buffer, e.Offset + 9, packageLength - 9);
                        e.SetBuffer(e.Offset, packageLength);
                        args.AcceptSocket.SendAsync(e);
                    }
                    CloseClientSocket(args);
                }
            }
            catch (Exception ee)
            {
                LogService.Instance.Log.Error("发送数据包过程出错！" + ee.ToString());
            }
        }

		/// <summary>
        /// 发送数据包成功后回收资源
        /// </summary>
        /// <param name="e"></param>
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                string userName = ((AsyncUserToken)e.UserToken).UserName;
                if (userName != null)
                {
                    CloseClientSocket(userMap[userName]);
                }
            }
            else
            {
                LogService.Instance.Log.Info("发送数据包失败！");
            }
            readWritePool.Push(e);
        }

        /// <summary>
        /// 释放连接异常的资源
        /// </summary>
        /// <param name="e"></param>
        private void CloseClientSocket(SocketAsyncEventArgs e)
        {
            LogService.Instance.Log.Info("释放args！");
            AsyncUserToken token = e.UserToken as AsyncUserToken;

            try
            {
                e.AcceptSocket.Shutdown(SocketShutdown.Send);
            }
            catch (Exception ee) 
            {
                LogService.Instance.Log.Info("shutdown socket出错！" + ee.ToString());
            }
            e.AcceptSocket.Close();
            string userName = ((AsyncUserToken)e.UserToken).UserName;
            ((AsyncUserToken)e.UserToken).UserName = null;
            Interlocked.Decrement(ref netCounter.currentConnectedSocketsNumber);
            m_maxNumberAcceptedClients.Release();
            readWritePool.Push(e);
            userMap.Remove(userName);
        }
    }
}
